#!/usr/bin/python2.7

import pygtk
import gtk
import gobject
import GraphDisplayer
import GraphicalGraph
from Dependency import *
import threading

import xml


class MainWindow(gtk.Window):
    def __init__(self, engine):
        self.engine = engine
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)

        # new main windo
        mainVbox = gtk.VBox()
        self.graph_displayer = GraphDisplayer.GraphDisplayer(self.engine)

        #menubar
        menu_bar = gtk.MenuBar()
        file_menu = gtk.Menu()
        settings_menu = gtk.Menu()

        # Menu association
        file_menu_item = gtk.MenuItem("File")
        new_project_menu_item = gtk.MenuItem("New project")
        close_menu_item = gtk.MenuItem("Quit")
        open_menu_item = gtk.MenuItem("Open")
        save_menu_item = gtk.MenuItem("Save")
        saveas_menu_item = gtk.MenuItem("Save As")
        import_menu_item = gtk.MenuItem("Import")
        separator_menu_item = gtk.SeparatorMenuItem()

        settings_menu_item = gtk.MenuItem("Settings")
        variables_menu_item = gtk.MenuItem("Variables")

        file_menu.append(new_project_menu_item)
        file_menu.append(open_menu_item)
        file_menu.append(save_menu_item)
        file_menu.append(saveas_menu_item)
        file_menu.append(import_menu_item)
        file_menu.append(separator_menu_item)
        file_menu.append(close_menu_item)
        file_menu_item.set_submenu(file_menu)
        
        settings_menu.append(variables_menu_item)
        settings_menu_item.set_submenu(settings_menu)
        menu_bar.append(file_menu_item)
        menu_bar.append(settings_menu_item)
 
        self.commands_output_textview = gtk.TextView()
        self.commands_output_buffer = gtk.TextBuffer()
        self.commands_output_textview.set_buffer(self.commands_output_buffer)
        self.commands_output_textview.set_size_request(-1, 100)
        self.commands_output_textview.connect('size-allocate', self.scrollupTextview)
        
        self.commands_scroll = gtk.ScrolledWindow()
        self.commands_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.commands_scroll.add(self.commands_output_textview)
        
        vpane = gtk.VPaned()
        vpane.add1(self.graph_displayer)
        vpane.add2(self.commands_scroll)
        position = int(800 * (5.0 / 6.0))
        vpane.set_position(position)
        
        menu_bar.get_allocation().height = 10
        
        toolbar = gtk.Toolbar()
        
        self.selectModeButton = gtk.RadioToolButton(None)
        self.selectModeButton.set_label("Select")
        self.editModeButton = gtk.RadioToolButton(self.selectModeButton)
        self.editModeButton.set_label("Edit")
        toolbar.insert(self.selectModeButton, 0)
        toolbar.insert(self.editModeButton, 1)
        
        self.selectModeButton.connect("clicked", self.enableSelectMode)
        self.editModeButton.connect("clicked", self.enableEditMode)
        
        mainVbox.pack_start(menu_bar, False, False, 0)
        #mainVbox.pack_start(toolbar, False, False, 0)
        mainVbox.pack_start(vpane, True, True, 0)
        self.add(mainVbox)

        self.set_title("Automated Build Tool - Main window")
        
        new_project_menu_item.connect("activate", self.createNewProject)
        close_menu_item.connect("activate", self.destroy)
        open_menu_item.connect("activate", self.openConfiguration)
        save_menu_item.connect("activate", self.saveConfiguration)
        saveas_menu_item.connect("activate", self.saveAsConfiguration)
        import_menu_item.connect("activate", self.importGraph)
        
        variables_menu_item.connect("activate", self.showVariablesViewer)
        
        self.graph_displayer.connect("execute_asked", self.execute)
        self.connect("destroy", self.destroy)
        
        self.resize(800, 800)
        self.setGraph(self.engine.graph)
        
        self.mode = GraphDisplayer.SELECT_MODE
        
        
        
    def destroy(self, widget):
        gtk.Window.destroy(self)
        gtk.main_quit()
        # stop all running threads
        for t in self.engine.threads:
            t.stop()


    def setGraph(self, graph):
        self.graph_displayer.setGraph(graph)
        
        
    def openConfiguration(self, widget):
        fileChooser = gtk.FileChooserDialog(title="Load project", action=gtk.FILE_CHOOSER_ACTION_OPEN)
        open_button = fileChooser.add_button("Open", 0)
        open_button = fileChooser.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        return_code = fileChooser.run()
        filename = fileChooser.get_filename()
        
        if return_code == 0 and not filename is None:
        # if successful
            
            self.engine.loadProject(filename)
            self.graph_displayer.setGraph(self.engine.graph)
        fileChooser.destroy()
        
        
    def createNewProject(self, widget):
        self.engine.newProject()
        self.graph_displayer.setGraph(self.engine.graph)
        self.graph_displayer.update()
        
    def saveConfiguration(self, widget):
        if self.engine.currentProjectFilename <> "":
            self.engine.saveProject(self.engine.currentProjectFilename)
        else:
            self.saveAsConfiguration(None)
        
    def saveAsConfiguration(self, widget):
        fileChooser = gtk.FileChooserDialog(title="Save project", action=gtk.FILE_CHOOSER_ACTION_SAVE)
        open_button = fileChooser.add_button("Save", 0)
        open_button = fileChooser.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        return_code = fileChooser.run()

        if return_code == 0:
        # if successful
            filename = fileChooser.get_filename()
            self.engine.saveProject(filename)
        fileChooser.destroy()
        
    def importGraph(self, widget):
        fileChooser = gtk.FileChooserDialog(title="Import file", action=gtk.FILE_CHOOSER_ACTION_OPEN)
        open_button = fileChooser.add_button("Open", 0)
        open_button = fileChooser.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        return_code = fileChooser.run()

        if return_code == 0:
        # if successful
            filename = fileChooser.get_filename()
            graph_name = filename.split("\\")[-1]
            self.engine.importGraph(filename)
            self.graph_displayer.setGraph(self.engine.graph)
            self.graph_displayer.arrangeGraph()
        fileChooser.destroy()
        
        
    def execute(self, widget, graph, dependency):
        self.engine.execute(graph, dependency, self.commands_output_textview)
        
     
    # called when the buffer has changed
    def scrollupTextview(self, widget, buffer):
        #adj = widget.get_vadjustment()
        #adj.set_value( adj.upper - adj.page_size )
        pass
        
        
    def showVariablesViewer(self, widget):
        window = VariableViewerDialog(self.engine.getGlobalVariables())
        window.add_button("Save", 0)
        window.add_button("Close", 1)
        
        finished = False
        while not finished:
            retcode = window.run()
            if retcode == 0:
                if window.checkSyntax():
                    self.engine.setGlobalVariables(window.getVariables())
                    finished = True
            else:
                finished = True
                
        window.destroy();
        

    def enableSelectMode(self, widget):
        if widget.get_active():
            self.mode = GraphDisplayer.SELECT_MODE
            self.graph_displayer.setMode(self.mode)
    
    def enableEditMode(self, widget):
        if widget.get_active():
            self.mode = GraphDisplayer.EDIT_MODE
            self.graph_displayer.setMode(self.mode)
    
    
class FilterWindow(gtk.Window):

    def __init__(self, graph_displayer):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        self.set_modal(True)
        self.graph_displayer = graph_displayer

        filters_hbox = gtk.HBox()
        self.no_children_checkbutton = gtk.CheckButton("No children")
        self.no_parents_checkbutton = gtk.CheckButton("No parents")
        filters_hbox.pack_start(self.no_children_checkbutton, True, True, 0)
        filters_hbox.pack_start(self.no_parents_checkbutton, True, True, 0)

        button_hbox = gtk.HBox()

        show_button = gtk.Button("Show")
        show_button.connect("clicked", self.showNodes)
        showall_button = gtk.Button("Show all")
        showall_button.connect("clicked", self.showAll)
        hide_button = gtk.Button("Hide")
        hide_button.connect("clicked", self.hideNodes)
        close_button = gtk.Button("Close")
        close_button.connect("clicked", self.destroy)
        
        
        button_hbox.pack_start(show_button, True, True, 0)
        button_hbox.pack_start(showall_button, True, True, 0)
        button_hbox.pack_start(hide_button, True, True, 0)
        button_hbox.pack_start(close_button, True, True, 0)


        main_box = gtk.VBox()
        main_box.pack_start(filters_hbox, False, True, 0)
        main_box.pack_start(button_hbox, False, True, 0)

        self.add(main_box)
        
        self.show_all()
        
    def destroy(self, widget):
        gtk.Window.destroy(self)
        
    
    def filter(self):
        graph = self.graph_displayer.graph
        nodes = graph.nodes.values()
        if self.no_children_checkbutton.get_active():
            nodes = [x for x in nodes if len(graph.children(x)) == 0]
            
        if self.no_parents_checkbutton.get_active():  
            nodes = [x for x in nodes if len(graph.parents(x)) == 0]
        return nodes 

        
    def showNodes(self, widget):
        nodes = self.filter()
        self.graph_displayer.showNodes(nodes)
        
    def hideNodes(self, widget):
        nodes = self.filter()
        self.graph_displayer.hideNodes(nodes)
        
    def showAll(self, widget):
        nodes = self.graph_displayer.graph.nodes.values()
        self.graph_displayer.showNodes(nodes)
    
    
class LineEdit(gtk.HBox):
    def __init__(self, label):
        gtk.HBox.__init__(self)
        self.label = gtk.Label(label)
        self.entry = gtk.Entry()
        self.pack_start(self.label, False, True, 0)
        self.pack_start(self.entry, True, True, 0)
        
            
    def set_text(self, text):
        self.entry.set_text(text)
        
    def get_text(self):
        return self.entry.get_text()

        
class DependencyTreeView(gtk.TreeView):

    def __init__(self):
        gtk.TreeView.__init__(self)
        self.dependency_model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING)
        self.set_model(self.dependency_model)

        node_name_column = gtk.TreeViewColumn('Dependency')
        # Allow sorting on the column
        node_name_column.set_sort_column_id(1)
        
        self.append_column(node_name_column)
        
        # create a CellRendererText to render the data
        nameCell = gtk.CellRendererText()
        # add the cell to the tvcolumn and allow it to expand
        node_name_column.pack_start(nameCell, True)
        node_name_column.add_attribute(nameCell, 'text', 1)
        
    def insertDependencies(self, dependencies):
        for dep in dependencies:
            self.dependency_model.append([dep, dep.name])
        
    def clear(self):
        self.dependency_model.clear()
        

class DependencyWidget(gtk.VBox):
    def __init__(self, dependency, graph):
        gtk.VBox.__init__(self)
        # Window on top, cannot access others
        
        self.dependency = dependency
        self.graph = graph
        
        
        self.dependency_name_lineedit = LineEdit("Name:")
        self.dependency_parents_lineedit = LineEdit("Parents:")
        self.dependency_children_lineedit = LineEdit("Children:")
        
        self.child_dependency_treeview = DependencyTreeView()
        child_scrolled = gtk.ScrolledWindow()
        child_scrolled.add(self.child_dependency_treeview)
        
        self.parent_dependency_treeview = DependencyTreeView()
        parent_scrolled = gtk.ScrolledWindow()
        parent_scrolled.add(self.parent_dependency_treeview)
        
        treeview_box = gtk.HBox()
        treeview_box.pack_start(child_scrolled, True, True, 0)
        treeview_box.pack_start(parent_scrolled, True, True, 0)
        
        commands_vbox = gtk.VBox()
        commands_label = gtk.Label("Commands")
        self.commands_textview = gtk.TextView()
        self.commands_buffer = gtk.TextBuffer()
        self.commands_textview.set_buffer(self.commands_buffer)
        
        commands_vbox.pack_start(commands_label, False, False, 0)
        commands_vbox.pack_start(self.commands_textview, True, True, 0)
        
        description_vbox = gtk.VBox()
        description_label = gtk.Label("Description")
        description_textview = gtk.TextView()
        
        
        description_vbox.pack_start(description_label, False, False, 0)
        description_vbox.pack_start(description_textview, True, True, 0)
        
        vbox = gtk.VBox()
        vbox.pack_start(self.dependency_name_lineedit, False, False, 0)
        vbox.pack_start(self.dependency_parents_lineedit, False, True, 0)
        vbox.pack_start(self.dependency_children_lineedit, False, True, 0)
        vbox.pack_start(treeview_box, True, True, 0)
        vbox.pack_start(commands_vbox, True, True, 0)
        vbox.pack_start(description_vbox, False, True, 0)
        
        self.pack_start(vbox)

        self.show_all()
        
        self.loadDependencyInformation()
        

    def getDependency(self):
        name = self.dependency_name_lineedit.get_text()
        iter = self.commands_buffer.get_start_iter()
        commands = []
        while not iter.is_end():
            end_line = iter.copy()
            end_line.forward_line()
            command = iter.get_text(end_line).strip()
            iter = end_line
            commands.append(command)
        dep = DependencyNode(name)
        dep.commands = commands
        return dep
    
    def loadDependencyInformation(self):
        parents = self.graph.parents(self.dependency)
        children = self.graph.children(self.dependency)
        self.dependency_name_lineedit.set_text(self.dependency.name)
        self.dependency_parents_lineedit.set_text(str(len(parents)))
        self.dependency_children_lineedit.set_text(str(len(children)))
        self.commands_buffer.set_text("\n".join(self.dependency.commands))
        
        self.child_dependency_treeview.insertDependencies(children)
        self.parent_dependency_treeview.insertDependencies(parents)
        
class DependencyWindow(gtk.Window):

    def __init__(self, dependency, graph):
        gtk.Window.__init__(self)
        self.widget = DependencyWidget(dependency, graph)
        self.set_modal(True)
        
        self.set_title("Dependency - "+dependency.name)
        self.add(self.widget)
        

        
class DependencyDialog(gtk.Dialog):
    def __init__(self, dependency, graph):
        gtk.Dialog.__init__(self, title="Dependency")
        self.widget = DependencyWidget(dependency, graph)
        
        vbox = self.get_content_area()
        vbox.pack_start(self.widget, True, True, 0)
        
        
    def getDependency(self):
        return self.widget.getDependency()
        
        
        
class VariableViewerDialog(gtk.Dialog):

    def __init__(self, variables):
        gtk.Dialog.__init__(self, title="Variable Editor")
        self.set_modal(True)
        
        vbox = self.get_content_area()
        
        self.variables_textview = gtk.TextView()
        self.variables_buffer = gtk.TextBuffer()
        self.variables_textview.set_buffer(self.variables_buffer)
        
        scrolledwindow = gtk.ScrolledWindow()
        scrolledwindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
        scrolledwindow.add(self.variables_textview)
        self.variables_textview.set_wrap_mode(gtk.WRAP_WORD)
        scrolledwindow.set_size_request(400, 300)
        
        vbox.pack_start(scrolledwindow, True, True, 0)    
        
        variables_str = [name+"="+value for (name, value) in variables.items()]
        self.variables_buffer.set_text("\n".join(variables_str))
        
        self.show_all()
        
        
    def checkSyntax(self):
        iter = self.variables_buffer.get_start_iter()
        variables = []
        while not iter.is_end():
            end_line = iter.copy()
            end_line.forward_line()
            variable = iter.get_text(end_line).strip()
            iter = end_line
            if not re.match("[A-Z]+=(.)*", variable):
                return False
        return True
        
        
    def getVariables(self):
        variables = {}
        iter = self.variables_buffer.get_start_iter()
        while not iter.is_end():
            end_line = iter.copy()
            end_line.forward_line()
            variable = iter.get_text(end_line).strip()
            iter = end_line
            result =  re.match("([A-Z]+)=(.*)", variable)
            if result:
                name = result.group(1)
                value = result.group(2)
                variables[name] = value
        return variables
        